//  Copyright Druid Mechanics


#include "Player/AuraPlayerController.h"
#include "EnhancedInputSubsystems.h"
#include "Interaction/EnemyInterface.h"
#include "GameplayTagContainer.h"
#include "Input/AuraInputComponent.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystem/AuraAbilitySystemComponent.h"
#include "Components/SplineComponent.h"
#include "AuraGameplayTags.h"
#include "NavigationSystem.h"
#include "Navigationpath.h"
#include "GameFramework/Character.h"
#include "UI/Widget/DamageTextComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "Actor/MagicCircle.h"
#include "Aura/Aura.h"
#include "Components/DecalComponent.h"
#include "Interaction/HighlightInterface.h"

AAuraPlayerController::AAuraPlayerController()
{
	/**
	 * bReplicates: 如果为true，此Actor将会复制到其他远程设备。
	 * 当服务器上开启复制的Actor或Property变化时，会将变化更新到客户端，反之，则不可以。
	 */
	bReplicates = true;

	Spline = CreateDefaultSubobject<USplineComponent>("Spline");
}

void AAuraPlayerController::BeginPlay()
{
	Super::BeginPlay();
	/**
	 * check: 判断为false将立即终止执行，仅在开发环境下执行。
	 */
	check(AuraContext);
	/**
	 * UEnhancedInputLocalPlayerSubsystem: 每一个玩家的输入子系统。
	 */
	/**
	 * ULocalPlayer: 在当前客户端或监听服务器上的每一个玩家都有一个LocalPlayer。
	 * LocalPlayer在进入地图就是活跃的，并且在splitscreen/coop的情况下有几个生成。
	 * 在专用服务器上将不存在。
	 */
	/**
	 * GetLocalPlayer: 如果控制器存在ULocalPlayer则返回，否则返回nullptr。
	 */
	UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(
		GetLocalPlayer());
	/**
	 * 什么时候用assert和什么时候用null判断:
	 * 当一定存在null时，用null判断，例如玩家控制器不是每台机器上都存在。
	 * 当存在null不对时，用assert。
	 */
	if (Subsystem)
	{
		/**
		 * AddMappingContext: 添加控制映射上下文。
		 * 推测: 值越小，优先级越高。
		 */
		Subsystem->AddMappingContext(AuraContext, 0);
	}
	/**
	 * bShowMouseCursor: 是否应该显示鼠标。
	 */
	bShowMouseCursor = true;
	/**
	 * DefaultMouseCursor； 鼠标显示的默认类型。
	 */
	DefaultMouseCursor = EMouseCursor::Default;

	FInputModeGameAndUI InputModeData;
	/**
	 * SetLockMouseToViewportBehavior: 设置鼠标在视口中的锁定行为。
	 * 
	 */
	InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
	/**
	 * SetHideCursorDuringCapture: 是否在由鼠标按下造成的短暂的鼠标捕获期间隐藏鼠标。
	 */
	InputModeData.SetHideCursorDuringCapture(false);
	/**
	 * SetInputMode: 设置输入模式。
	 */
	SetInputMode(InputModeData);
}

void AAuraPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();
	/**
	 * TObjectPtr<class UInputComponent> InputComponent
	 * 处理此Actor输入的组件，如果此Actor可用。
	 */
	/**
	 * CastChecked: 转换方法，若转换成功，将返回非空指针或引用；若转换失败，将出发类似check的断言，直接终止程序执行；在开发环境会进行转换，但是不会崩溃。
	 * LINK: https://blog.csdn.net/ttod/article/details/136060891
	 */
	/**
	 * UEnhancedInputComponent: 
	 * Actor输入绑定的实现。
	 * 增强输入组件是一个瞬态组件，它使Actor能够将增强操作绑定到委派功能，或监视这些操作。
	 * 输入组件由PlayerController管理的堆栈处理，并由PlayerInput处理。
	 * 增强操作绑定不会消耗输入事件，但是使用UInputMappingContext::Priority复制行为。
	 */
	UAuraInputComponent* AuraInputComponent = CastChecked<UAuraInputComponent>(InputComponent);
	/**
	 * 猜测，在蓝图中将UInputAction添加到UInputMappingContext的数组当中(相当于注册)，在UEnhancedInputLocalPlayerSubsystem中添加映射，
	 * 最后在APlayerController中或ACharacter中的EnhancedInputComponent中绑定UInputAction的行为造成的结果。
	 */
	/**
	 * BindAction: 将与任何处理程序签名匹配的委托函数绑定到通过UInputMappingContext分配给此组件所有者的UInputAction。
	 */
	AuraInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AAuraPlayerController::Move);
	AuraInputComponent->BindAction(ShiftAction, ETriggerEvent::Started, this, &AAuraPlayerController::ShiftPressed);
	AuraInputComponent->BindAction(ShiftAction, ETriggerEvent::Completed, this, &AAuraPlayerController::ShiftReleased);
	AuraInputComponent->BindAbilityActions(InputConfig, this, &ThisClass::AbilityInputTagPressed,
	                                       &ThisClass::AbilityInputTagReleased, &ThisClass::AbilityInputTagHeld);
}

void AAuraPlayerController::PlayerTick(float DeltaTime)
{
	Super::PlayerTick(DeltaTime);
	CursorTrace();
	AutoRun();
	UpdateMagicCircleLocation();
}

void AAuraPlayerController::ShowMagicCircle(UMaterialInterface* DecalMaterial)
{
	if (!IsValid(MagicCircle))
	{
		MagicCircle = GetWorld()->SpawnActor<AMagicCircle>(MagicCircleClass);
		if (DecalMaterial)
		{
			MagicCircle->MagicCircleDecal->SetMaterial(0, DecalMaterial);
		}
	}
}

void AAuraPlayerController::HideMagicCircle()
{
	if (IsValid(MagicCircle))
	{
		MagicCircle->Destroy();
	}
}

UAuraAbilitySystemComponent* AAuraPlayerController::GetASC()
{
	if (AuraAbilitySystemComponent == nullptr)
	{
		/**
		 * 这里的方式很好啊，直接从GAS蓝图库里调用。
		 */
		AuraAbilitySystemComponent = Cast<UAuraAbilitySystemComponent>(
			UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetPawn<APawn>()));
	}
	return AuraAbilitySystemComponent;
}

void AAuraPlayerController::AutoRun()
{
	if (!bAutoRunning)
	{
		return;
	}
	if (APawn* ControlledPawn = GetPawn())
	{
		/**
		 * FindLocationClosestToWorldLocation: 获取在世界中的位置，返回值在曲线中离这个位置最近的点。
		 */
		const FVector LocationOnSpline = Spline->FindLocationClosestToWorldLocation(
			ControlledPawn->GetActorLocation(), ESplineCoordinateSpace::World);
		/**
		 * FindDirectionClosestToWorldLocation: 获取在世界中的位置，返回值在曲线中离这个位置最近的点方向。
		 */
		const FVector Direction = Spline->FindDirectionClosestToWorldLocation(
			ControlledPawn->GetActorLocation(), ESplineCoordinateSpace::World);
		ControlledPawn->AddMovementInput(Direction);
		const float DistanceToDestination = (LocationOnSpline - CachedDestination).Length();
		if (DistanceToDestination <= AutoRunAcceptanceRadius)
		{
			bAutoRunning = false;
		}
	}
}

void AAuraPlayerController::UpdateMagicCircleLocation()
{
	if (IsValid(MagicCircle))
	{
		MagicCircle->SetActorLocation(CursorHit.ImpactPoint);
	}
}


void AAuraPlayerController::Move(const FInputActionValue& InputActionValue)
{
	if (GetASC() && GetASC()->HasMatchingGameplayTag(FAuraGameplayTags::Get().Player_Block_InputPressed))
	{
		return;
	}
	const FVector2D InputAxisVector = InputActionValue.Get<FVector2D>();
	/**
	 * GetControlRotation: 获取控制器角度。这是一个全瞄准角度，也许和摄像机朝向角不同。
	 */
	const FRotator Rotation = GetControlRotation();
	const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
	/**
	 * 猜测: FRotationMatrix是旋转角矩阵。
	 * GetUnitAxis: 返回矩阵指定方向的长度单元。
	 */
	const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
	/**
	 * GetPawn<>: 返回GetPawn的模板版本，如果转换失败则返回nullptr。
	 * 这里有炫技的成分，是我层次太低，之前想肤浅了。
	 */
	if (APawn* ControlledPawn = GetPawn<APawn>())
	{
		/**
		 * AddMovementInput: 
		 * 添加移动输入沿着给予的世界方向矢量(通常是标准值)按ScaleValue缩放。如果ScaleValue小于0，将会向着相反方向移动。
		 * 基本Pawn类不会自动请求移动，它取决于用户在Tick事件中的操作。像Character和DefaultPawn等子类将自动处理输入和移动。
		 */
		ControlledPawn->AddMovementInput(ForwardDirection, InputAxisVector.Y);
		ControlledPawn->AddMovementInput(RightDirection, InputAxisVector.X);
	}
}

void AAuraPlayerController::HighlightActor(AActor* InActor)
{
	if (IsValid(InActor) && InActor->Implements<UHighlightInterface>())
	{
		IHighlightInterface::Execute_HighlightActor(InActor);
	}
}

void AAuraPlayerController::UnHighlightActor(AActor* InActor)
{
	if (IsValid(InActor) && InActor->Implements<UHighlightInterface>())
	{
		IHighlightInterface::Execute_UnHighlightActor(InActor);
	}
}

void AAuraPlayerController::CursorTrace()
{
	/**
	 * HasMatchingGameplayTag: 检查计数容器是否有与具体标签匹配的游戏标签(扩展到父类的资产标签)。
	 */
	if (GetASC() && GetASC()->HasMatchingGameplayTag(FAuraGameplayTags::Get().Player_Block_CursorTrace))
	{
		UnHighlightActor(LastActor);
		UnHighlightActor(ThisActor);
		LastActor = nullptr;
		ThisActor = nullptr;
		return;
	}
	const ECollisionChannel TraceChannel = IsValid(MagicCircle) ? ECC_ExcludePlayers : ECC_Visibility;
	/**
	 * GetHitResultUnderCursor: 获取在鼠标下时(按下或悬浮)获取鼠标碰撞参数。
	 */
	GetHitResultUnderCursor(TraceChannel, false, CursorHit);
	if (!CursorHit.bBlockingHit)
	{
		return;
	}
	LastActor = ThisActor;
	/**
	 * AActorObj->Implements<T>(): 判断此Actor是否继承自某接口。
	 */
	if (IsValid(CursorHit.GetActor()) && CursorHit.GetActor()->Implements<UHighlightInterface>())
	{
		ThisActor = CursorHit.GetActor();
	}
	else
	{
		ThisActor = nullptr;
	}
	/**
	 * 从鼠标发出的射线，有这样几种情况:
	 * LastActor == 空的 && ThisActor == 空的 => 什么都不做。
	 * LastActor == 空的 && ThisActor == 非空 => 高亮ThisActor。
	 * LastActor == 非空 && ThisActor == 空的 => 取消LastActor高亮。
	 * LastActor == 非空 && ThisActor == 非空 && LastActor != ThisActor => 取消LastActor高亮，高亮ThisActor。
	 * LastActor == 非空 && ThisActor == 非空 && LastActor == ThisActor => 什么都不做。
	 */
#pragma region 敌人情况处置一
	// if (LastActor == nullptr)
	// {
	// 	if (ThisActor != nullptr)
	// 	{
	// 		ThisActor->HighlightActor();
	// 	}
	// 	else
	// 	{
	// 		/**
	// 		 * LastActor == 空的 && ThisActor == 空的 => 什么都不做。
	// 		 */
	// 	}
	// }
	// else
	// {
	// 	if (ThisActor == nullptr)
	// 	{
	// 		LastActor->UnHighlightActor();
	// 	}
	// 	else
	// 	{
	// 		if (ThisActor != LastActor)
	// 		{
	// 			LastActor->UnHighlightActor();
	// 			ThisActor->HighlightActor();
	// 		}
	// 		else
	// 		{
	// 			/**
	// 			 * LastActor == 非空 && ThisActor == 非空 && LastActor == ThisActor => 什么都不做。
	// 			 */
	// 		}
	// 	}
	// }
#pragma endregion
	if (LastActor != ThisActor)
	{
		UnHighlightActor(LastActor);
		HighlightActor(ThisActor);
	}
}

void AAuraPlayerController::AbilityInputTagPressed(FGameplayTag InputTag)
{
	if (GetASC() && GetASC()->HasMatchingGameplayTag(FAuraGameplayTags::Get().Player_Block_InputPressed))
	{
		return;
	}
	if (InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
	{
		if(IsValid(ThisActor))
		{
			TargetingStatus = ThisActor->Implements<UEnemyInterface>() ? ETargetingStatus::TargetingEnemy : ETargetingStatus::TargetingNonEnemy;
			bAutoRunning = false;
		}else
		{
			TargetingStatus = ETargetingStatus::NoTargeting;
		}
		bAutoRunning = false;
	}
	if (GetASC())
	{
		GetASC()->AbilityInputTagPressed(InputTag);
	}
}

void AAuraPlayerController::AbilityInputTagHeld(FGameplayTag InputTag)
{
	if (GetASC() && GetASC()->HasMatchingGameplayTag(FAuraGameplayTags::Get().Player_Block_InputHeld))
	{
		return;
	}
	if (!InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
	{
		if (GetASC())
		{
			GetASC()->AbilityInputTagHeld(InputTag);
		}
		return;
	}
	if (TargetingStatus == ETargetingStatus::TargetingEnemy || bShiftKeyDown)
	{
		if (GetASC())
		{
			GetASC()->AbilityInputTagHeld(InputTag);
		}
	}
	else
	{
		FollowTime += GetWorld()->GetDeltaSeconds();
		if (CursorHit.bBlockingHit)
		{
			CachedDestination = CursorHit.ImpactPoint;
		}
		if (APawn* ControlledPawn = GetPawn())
		{
			const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
			ControlledPawn->AddMovementInput(WorldDirection);
		}
	}
}

void AAuraPlayerController::AbilityInputTagReleased(FGameplayTag InputTag)
{
	if (GetASC() && GetASC()->HasMatchingGameplayTag(FAuraGameplayTags::Get().Player_Block_InputReleased))
	{
		return;
	}
	if (!InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
	{
		if (GetASC())
		{
			GetASC()->AbilityInputReleased(InputTag);
		}
		return;
	}
	if (GetASC())
	{
		GetASC()->AbilityInputReleased(InputTag);
	}
	if (TargetingStatus != ETargetingStatus::TargetingEnemy && !bShiftKeyDown)
	{
		const APawn* ControlledPawn = GetPawn();
		if (FollowTime <= ShortPressThreshold && ControlledPawn)
		{
			if(IsValid(ThisActor) && ThisActor->Implements<UHighlightInterface>())
			{
				IHighlightInterface::Execute_SetMoveToLocation(ThisActor, CachedDestination);
			}else if (GetASC() && !GetASC()->HasMatchingGameplayTag(FAuraGameplayTags::Get().Player_Block_InputPressed))
			{
				UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, ClickNiagaraSystem, CachedDestination);
			}
			
			if (UNavigationPath* NavPath = UNavigationSystemV1::FindPathToLocationSynchronously(
				this, ControlledPawn->GetActorLocation(), CachedDestination))
			{
				/**
				 * ClearSplinePoints: 清空样条线所有点。
				 */
				Spline->ClearSplinePoints();
				for (const FVector& PointLoc : NavPath->PathPoints)
				{
					/**
					 * AddSplinePoint: 添加点到样条线。
					 * ESplineCoordinateSpace::World&SplineCoordinateSpace::Local: 函数接受的空间作标签类型。
					 */
					Spline->AddSplinePoint(PointLoc, ESplineCoordinateSpace::World);
				}
				if (NavPath->PathPoints.Num() > 0)
				{
					CachedDestination = NavPath->PathPoints[NavPath->PathPoints.Num() - 1];
					bAutoRunning = true;
				}
			}
		}
		FollowTime = 0.f;
		TargetingStatus = ETargetingStatus::NoTargeting;
	}
}

void AAuraPlayerController::ShowDamageNumber_Implementation(float DamageAmount, ACharacter* TargetCharacter,
                                                            bool bBlockedHit, bool bCriticalHit)
{
	if (IsValid(TargetCharacter) && DamageTextComponentClass && IsLocalController())
	{
		/**
		 * 这里让我知道了，T* NewObject(UObject* Outer, const UClass* Class,...): 第一个参数Outer不是随便传的，指的是Component生成在那个Actor上。
		 */
		UDamageTextComponent* DamageText = NewObject<UDamageTextComponent>(TargetCharacter, DamageTextComponentClass);
		/**
		 * RegisterComponent: 注册组件，创建任意渲染/物理状态。如果不存在，将添加自己到输出的Actor数组中。
		 * NOTE: 因为在构造函数之外创建的，所以手动注册组件。
		 */
		DamageText->RegisterComponent();
		DamageText->AttachToComponent(TargetCharacter->GetRootComponent(),
		                              FAttachmentTransformRules::KeepRelativeTransform);
		DamageText->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
		DamageText->SetDamageText(DamageAmount, bBlockedHit, bCriticalHit);
	}
}
